package com.ar4j.type;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

/**
 * A registry of default type converters for active record beans
 */
public final class TypeConverterRegistry {
  
  private static TypeConverterRegistry instance;
  private static ReentrantLock instanceLock = new ReentrantLock();
  
  /**
   * Retrieve the singleton instance of type converter registry, or create one if it does not yet exist 
   */
  public static TypeConverterRegistry getInstance() {
    if(instance == null) {
      instanceLock.lock();
      try {
        if(instance == null) {
          instance = new TypeConverterRegistry();
        }
      } finally {
        instanceLock.unlock();
      }
    }
    
    return instance;
  }
  
  private Map<Class<?>, ITypeConverter<?, ?>> registry;
  
  private TypeConverterRegistry() {
    registry = Collections.synchronizedMap(new HashMap<Class<?>, ITypeConverter<?, ?>>());
  }
    
  /**
   * @return the type converter registered for the given type (or a parent class of the given type) or null
   * if one does not exist
   */
  public ITypeConverter<?, ?> getDefaultTypeConverter(Class<?> type) {
    
    // try direct lookup first
    ITypeConverter<?, ?> out = registry.get(type);
    
    // if we didn't find any, try to find assignable types
    if(out == null) {
      for(Map.Entry<Class<?>, ITypeConverter<?, ?>> entry : registry.entrySet()) {
        if(entry.getKey().isAssignableFrom(type)) {
          if(out != null) {
            throw new IllegalStateException("More than one type converter candidate found for type: " + type);          
          }
          
          out = entry.getValue();
        }
      }
    }
    
    return out;
  }
  
  /**
   * Register a type converter for the given type and all subclasses of the given type
   */
  public TypeConverterRegistry registerDefaultTypeConverter(Class<?> type, ITypeConverter<?, ?> converter) {
    registry.put(type, converter);
    return this;
  }
  
}
